package yawning.mew.bear;

import yawning.mew.ProcEffect.Trigger;
import yawning.mew.character.CharacterModel.AttackResult;
import yawning.mew.sim.SimulationAction;
import yawning.mew.sim.SimulationEventAction;

public class ThrashAction extends SimulationEventAction {
	BearThreatSimulator mState;
	double mCooldownExpireTime = 0.0;

	private double mAttackPower;
	private double mPCrit;
	private double mEnrageMult;
	private double mToTTMult;

	ThrashAction(BearThreatSimulator state) {
		super(state.mSimEngine);

		mState = state;
	}

	@Override
	public double perform() {
		int cost = mState.mModel.thrashCost;

		if (!mState.hasRage(cost) && !mState.mOoCEv.isUp()) return SimulationAction.ACTION_FAIL;
		if (mCooldownExpireTime > mState.mSimEngine.getSimTime()) return SimulationAction.ACTION_FAIL;

		AttackResult result = mState.mModel.getFrontalYellowResult();

		if (result == AttackResult.MISS) {
			mNrThrashMiss++;

			if (mState.mIsLogging) mState.mLog.log("Thrash: MISS\n");
		} else if (result == AttackResult.DODGE) {
			mNrThrashDodge++;

			if (mState.mIsLogging) mState.mLog.log("Thrash: DODGE\n");
		} else if (result == AttackResult.PARRY) {
			mNrThrashParry++;

			if (mState.mIsLogging) mState.mLog.log("Thrash: PARRY\n");
		} else {
			double damage = mState.mModel.mLevelDep.getThrashDmg() + mState.mModel.getAttackPower() * mState.mModel.mLevelDep.getThrashDmgCoeff();

			if (result == AttackResult.CRIT) {
				mNrThrashCrit++;
				damage *= mState.mModel.multCrit;
			} else {
				mNrThrashHit++;
			}

			// Not affected by armor. (Last tested 2011/07/04 by Floofles EU-Silvermoon)
			damage *= mState.getBleedDamageMult();
			damage *= mState.mEnrageAction.getMult();
			damage *= mState.mToTTEv.getMult();

			// Snapshot the stats.
			mAttackPower = mState.mModel.getAttackPower();
			mPCrit = mState.mModel.getMeleeCritChance();
			mEnrageMult = mState.mEnrageAction.getMult();
			mToTTMult = mState.mToTTEv.getMult();

			if (!isUp())
				schedule(mState.mModel.thrashDebuffDuration, mState.mModel.thrashTickInterval);
			else
				extendExpiry(mState.mModel.thrashDebuffDuration + getNextTickTime() - getExpiryTime());

			mThrashDamage += Math.round(damage);

			if (mState.mIsLogging) mState.mLog.log("Thrash: %s Damage: %d\n", result, Math.round(damage));

			if (result == AttackResult.CRIT) {
				mState.mSavageDefenseEv.onTrigger();
				mState.mLotPEv.onTrigger();
				mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOWCRIT);
			} else {
				mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOW);
			}
		}

		mCooldownExpireTime = mState.mSimEngine.getSimTime() + mState.mModel.thrashCDDuration;

		mState.onRageLoss(cost);

		if (result == AttackResult.CRIT) mState.onPrimalFuryTrigger();

		return 1.5;
	}

	@Override
	public void onTick() {
		double damage = mState.mModel.mLevelDep.getThrashDoTDmg() + mAttackPower * mState.mModel.mLevelDep.getThrashDoTCoeff();
		AttackResult result = mState.mModel.getDotResult(mPCrit);

		if (result == AttackResult.CRIT) {
			mNrThrashDoTCrit++;
			damage *= mState.mModel.multCrit;
			mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.DOTCRIT);
		} else {
			mNrThrashDoTHit++;
			mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.DOT);
		}

		damage *= mState.getBleedDamageMult();
		damage *= mEnrageMult;
		damage *= mToTTMult;
		mThrashDoTDamage += Math.round(damage);

		if (mState.mIsLogging) mState.mLog.log("Thrash Tick: %s Damage: %d\n", result, Math.round(damage));
	}

	@Override
	public void onExpire() {
		if (mState.mIsLogging) mState.mLog.log("Thrash Expiration\n");
	}

	long mThrashDamage = 0;
	int mNrThrashMiss = 0;
	int mNrThrashDodge = 0;
	int mNrThrashParry = 0;
	int mNrThrashCrit = 0;
	int mNrThrashHit = 0;
	long mThrashDoTDamage = 0;
	int mNrThrashDoTCrit = 0;
	int mNrThrashDoTHit = 0;
}
